#include "ServerConnection.h"
#include "../../utility/format/Format.h"
#include <cstring>
#include <stdexcept>

using namespace std;
using namespace format;
using namespace network;

#if defined(WIN32) || defined(_WIN32)

bool ServerConnection::setPortReuable(bool on) const {
    int flag = 1;
    int ret = setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, (const char *) &flag, sizeof(int));
    return ret == 0;
}

Socket ServerConnection::accept() {
    Address client_address;
    socklen_t len = sizeof(*client_address.getSockAddr());
    int client_fd = ::accept(fd_, client_address.getSockAddr(), &len);
    if (client_fd == -1) {
        int error = WSAGetLastError();
        if (error == WSAEWOULDBLOCK || error == WSAEINPROGRESS || error == WSAEALREADY) {
            throw runtime_error("no immediately available connection requests");
        }
        throw runtime_error("accept failed with error: " + to_string(error));
    }
    return Socket(client_fd, client_address);
}

#else

bool ServerConnection::setPortReuable(bool on) const {
    int flag = 1;
    int ret = setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int));
    return ret == 0;
}

Socket ServerConnection::accept() {
    Address client_address;
    socklen_t len = sizeof(*client_address.getSockAddr());
    int client_fd = ::accept(fd_, client_address.getSockAddr(), &len);
    if (client_fd == -1) {
        if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
            throw runtime_error("no immediately available connection requests");
        }
        throw runtime_error(Format("accept failed with error: {0}", strerror(errno)));
    }
    return Socket(client_fd, client_address);
}

#endif

bool ServerConnection::bind() {
    sockaddr_in *addr = getSockAddrIn();
    addr->sin_addr.s_addr = htonl(INADDR_ANY);
    bool ret = ::bind(fd_, getSockAddr(), sizeof(*getSockAddr()));
    return !ret;
}

bool ServerConnection::listen(int backlog) const {
    bool ret = ::listen(fd_, backlog);
    return !ret;
}
